package model;

import java.util.*;
import math.Expression;
import model.altarica_fields.*;

public class AltaRicaNode {

    private String name;
    private HashMap<String, Var> states;
    private HashMap<String, Var> flow;
    private HashMap<String, Event> events;
    private List<UpdateExpression> init;
    private List<Trans> trans_list;
    private List<Expression> assert_list;
    private HashMap<String, AltaRicaNode> subs;
    private LinkedList<Sync> sync_list;
    private int line = 0;

    public AltaRicaNode() {

        this.name = null;
        this.states = null;
        this.flow = null;
        this.events = null;
        this.init = null;
        this.trans_list = null;
        this.assert_list = null;
        this.subs = new HashMap<String, AltaRicaNode>();
        this.sync_list = new LinkedList<Sync>();
    }

    public AltaRicaNode(String name) {

        this.name = name;

        this.states = null;
        this.flow = null;
        this.events = null;
        this.init = null;
        this.trans_list = null;
        this.assert_list = null;
        this.subs = new HashMap<String, AltaRicaNode>();
    }

    public List<Sync> getSyncs() {
        return Collections.unmodifiableList(this.sync_list);
    }

    public void setName(String name) {

        this.name = name;
    }

    public String getName() {

        return this.name;
    }

    public void setStates(HashMap<String, Var> states) {

        this.states = states;
    }

    public boolean hasStates() {
        return this.states != null && !this.states.values().isEmpty();
    }
    public boolean hasSubs() {
        return this.subs != null && !this.subs.values().isEmpty();
    }
    public boolean hasFlows() {
        return this.flow != null && !this.flow.values().isEmpty();
    }
    public boolean hasEvents() {
        return this.events != null && this.events.values().size()>1;
        
    }
    public boolean hasTrans() {
        return this.trans_list != null && !this.trans_list.isEmpty();
    }
    public boolean hasSyncs() {
        return this.sync_list != null && !this.sync_list.isEmpty();
    }
    public boolean hasInit() {
        return this.init != null && !this.init.isEmpty();
    }
    public boolean hasAsserts() {
        return this.assert_list != null && !this.assert_list.isEmpty();
    }
    public Collection<Var> getStates() {
        return this.states.values();
    }

    public Collection<Var> getFlows() {
        return this.flow.values();
    }

    public void setFlows(HashMap<String, Var> flow) {

        this.flow = flow;
    }

    public Collection<Event> getEvents() {
        return this.events.values();
    }

    public void setEvents(HashMap<String, Event> events) {

        this.events = events;
    }

    public Var getStateVar(String str) {

        return states.get(str);
    }

    public Var getFlowVar(String str) {

        return flow.get(str);
    }

    public Event getEvent(Event e) {

        return getEvent(e.getId());
    }

    public Event getEvent(String id) {

        return events.get(id);
    }

    public Var getVar(String str) {

        Var var = getStateVar(str);

        if (var == null) {
            var = getFlowVar(str);
        }

        return var;
    }

    public void setInit(List<UpdateExpression> list) {

        this.init = list;
    }

    public List<UpdateExpression> getInit() {

        return Collections.unmodifiableList(this.init);
    }
    
    
    

    public Set<String> getSubs() {
        return this.subs.keySet();
    }

    public void setTrans(List<Trans> trans) {

        this.trans_list = trans;
    }

    public List<Trans> getTrans() {

        return Collections.unmodifiableList(this.trans_list);
    }

    public void setAsserts(List<Expression> assert_list) {

        this.assert_list = assert_list;
    }

    public List<Expression> getAsserts() {

        return Collections.unmodifiableList(this.assert_list);
    }

    
    private AltaRicaNode _parent = null;
    
 
    public void setParentNode(AltaRicaNode parent) {
    	this._parent = parent;
    }
    
    public void addSub(String id, AltaRicaNode node) {
        this.subs.put(id, node);
        node.setParentNode(this);
       
    }

    public AltaRicaNode getSub(String id) {

        return this.subs.get(id);
    }

    public void addSync(Sync sync) {

        this.sync_list.addLast(sync);
    }

   
    @Override
    public String toString() {
    
    	return this.getName();
    }
    public String string() {

        StringBuilder builder = new StringBuilder();
        builder.append("node " + name + "\n");

        if (hasSubs()) {
            builder.append("\tsub\n");
            for (String s : subs.keySet()) {
            	if (subs.get(s) != null) {
            		builder.append("\t\t" + s + " : " + subs.get(s).getName() + ";\n");
            	}
            }
        }

        if (hasStates()) {
            builder.append("\tstate\n");
            for (Var s : states.values()) {
                builder.append("\t\t" + s + ";\n");
            }
        }


        if (hasFlows()) {
            builder.append("\tflow\n");
            for (Var s : flow.values()) {
                builder.append("\t\t" + s + ";\n");
            }
        }

        if (hasEvents()) {
            builder.append("\tevent\n");

            for (Event e : events.values()) {
            	
                if (e.isEpsilon() == false) {
                    builder.append("\t\t" + e.getId() + "; \n");
                }
                for (Event a : e.getLessEvents()) {
                    builder.append("\t\t" + a.getId() + " < " + e.getId() + "; \n");
                }
            }
        }

        if (hasTrans()) {
            builder.append("\ttrans\n");
            for (Trans t : trans_list) {
                builder.append("\t\t" + t.toString() + ";\n");
            }
        }

        if (hasSyncs()) {
            builder.append("\tsync\n");
            for (Sync sync : sync_list) {

                builder.append("\t\t" + sync.toString() + ";\n");
            }
        }

        if (hasInit()) {
            builder.append("\tinit\n");
            for (UpdateExpression u : init) {
                builder.append("\t\t" + u + ";\n");
            }
        }
        if (hasAsserts()) {
            builder.append("\tassert\n");
            for (Expression e : assert_list) {
                builder.append("\t\t" + e + ";\n");
            }
        }

        builder.append("edon\n");

        return builder.toString();
    }

	public void setLine(int line) {
		this.line = line;
	}

	public int getLine() {
		return line;
	}
}
